package edu.washington.cs.sounddetector.server.util;

import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Transaction;
import javax.sound.sampled.AudioFileFormat.Type;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;

import weka.classifiers.Classifier;
import edu.washington.cs.sounddetector.server.api.object.Notification;
import edu.washington.cs.sounddetector.server.api.object.PeakEvent;
import edu.washington.cs.sounddetector.server.api.object.SoundClass;
import edu.washington.cs.sounddetector.server.api.object.SoundFormat;
import edu.washington.cs.sounddetector.server.database.DbSoundClass;
import edu.washington.cs.sounddetector.server.database.Model;
import edu.washington.cs.sounddetector.server.database.Notifications;
import edu.washington.cs.sounddetector.server.database.ReceiverPhone;
import edu.washington.cs.sounddetector.server.database.Sound;
import edu.washington.cs.sounddetector.server.sound.SoundHelper;

/**
 * Utility class for inspecting the database of a running instance.
 * 
 * @author the.dylan.price@gmail.com
 * 
 */
public class InspectDb
{

    private PersistenceManagerFactory pmf;

    public InspectDb() throws SecurityException, NoSuchMethodException, IllegalArgumentException,
            InstantiationException, IllegalAccessException
    {
        pmf = JDOHelper.getPersistenceManagerFactory("datanucleus.properties");
    }

    public void writeSounds(String detectorId) throws Exception
    {
        PersistenceManager pm = pmf.getPersistenceManager();
        System.out.println("Querying database...");
        Collection<Sound> sounds = Sound.query(pm, "1", detectorId);

        if (sounds.isEmpty())
            System.out.println("No sounds found.");

        for (Sound sound : sounds)
        {
            if (sound.getSound() != null)
            {
                String name = String.format("%s-%s-%d.wav", sound.getSoundClass().getDetectorId(),
                        sound.getSoundClass().getSoundClass(), sound.getId());
                System.out.println("Writing sound file: " + name);
                writeSound(sound.getSound(), name);
            }
            else
            {
                System.out.println("Sound bytes were null");
            }
        }
    }

    private void writeSound(byte[] sound, String filename) throws Exception
    {
        SoundFormat soundFormat = new SoundFormat(44100, 16, 1, true, true);
        //SoundFormat soundFormat = Helper.getSoundFormat(Data.Doorbell.D1.getPath());
        AudioFormat format = SoundHelper.convert(soundFormat);

        ByteArrayInputStream bais = new ByteArrayInputStream(sound);
        AudioInputStream stream = new AudioInputStream(bais, format, sound.length);

        FileOutputStream out = new FileOutputStream(filename);

        AudioSystem.write(stream, Type.WAVE, out);
    }

    private void writeNotifications(String phoneId)
    {
        PersistenceManager pm = pmf.getPersistenceManager();
        System.out.println("Querying database...");
        ReceiverPhone phone = ReceiverPhone.query(pm, "1", phoneId);
        Collection<Notifications> notifications = Notifications.query(pm, "1", phone);
        if (notifications.isEmpty())
            System.out.println("Not notifications found.");
        for (Notifications notification : notifications)
        {
            SoundClass sc = new SoundClass(notification.getSoundClass().getSoundClass(),
                    notification.getSoundClass().getRgbColor());
            Notification not = new Notification(sc, notification.getSoundClass().getDetectorId(),
                    notification.getPeakEvents());
            System.out.println(not);
        }
    }

    private void writeModel(String detectorId)
    {
        PersistenceManager pm = pmf.getPersistenceManager();
        Model model = Model.query(pm, "1", detectorId);
        Classifier classifier = model.getClassifier();
        System.out.println(classifier.toString());
    }

    private void insertTestData(String phoneId)
    {
        PersistenceManager pm = pmf.getPersistenceManager();
        Transaction tx = pm.currentTransaction();

        try
        {
            tx.begin();

            ReceiverPhone phone = getTestPhone(pm, phoneId);

            DbSoundClass knockClass = getDbSoundClass(pm, "Living Room", "Knock", 65280);
            List<PeakEvent> knock = new ArrayList<PeakEvent>();
            knock.add(new PeakEvent(0, 56, 0.12));
            knock.add(new PeakEvent(0.5, 54, 0.11));
            knock.add(new PeakEvent(1, 58, 0.11));
            pm.makePersistent(new Notifications(knockClass, phone, knock));

            DbSoundClass microwaveClass = getDbSoundClass(pm, "Kitchen", "Microwave", 65280);
            List<PeakEvent> microwave = new ArrayList<PeakEvent>();
            microwave.add(new PeakEvent(0, 20, 0.72));
            microwave.add(new PeakEvent(1, 19, 0.71));
            microwave.add(new PeakEvent(2, 22, 0.73));
            microwave.add(new PeakEvent(3, 18, 0.75));
            pm.makePersistent(new Notifications(microwaveClass, phone, microwave));

            DbSoundClass screamClass = getDbSoundClass(pm, "Living Room", "Scream", 16711680);
            List<PeakEvent> scream = new ArrayList<PeakEvent>();
            scream.add(new PeakEvent(0, 80, 2));
            pm.makePersistent(new Notifications(screamClass, phone, scream));

            tx.commit();
        }
        finally
        {
            if (tx.isActive())
            {
                tx.rollback();
                System.out.println("ERROR: transaction unsuccessful, rolling back changes");
            }
            else
            {
                System.out.println("Success!");
            }
            pm.close();
        }
    }

    private ReceiverPhone getTestPhone(PersistenceManager pm, String phoneId)
    {
        ReceiverPhone test = ReceiverPhone.query(pm, "1", phoneId);
        if (test == null)
        {
            test = new ReceiverPhone(phoneId, null);
            pm.makePersistent(test);
        }
        return test;
    }

    private DbSoundClass getDbSoundClass(PersistenceManager pm, String detectorId,
            String soundClass, int rgbColor)
    {
        DbSoundClass sc = DbSoundClass.query(pm, "1", soundClass, detectorId);
        if (sc == null)
        {
            sc = new DbSoundClass(soundClass, detectorId);
            sc.setRgbColor(rgbColor);
            pm.makePersistent(sc);
        }
        return sc;
    }

    public static void main(String[] args) throws Exception
    {
        InspectDb ts = new InspectDb();

        if (args.length < 2)
        {
            System.out.println("Usage:");
            System.out.println("sounds detectorId");
            System.out.println("model detectorId");
            System.out.println("notifications phoneId");
            System.out.println("insertTestData phoneId");
            return;
        }

        String command = args[0];
        if (command.equals("sounds"))
        {
            ts.writeSounds(args[1]);
        }
        else if (command.equals("notifications"))
        {
            ts.writeNotifications(args[1]);
        }
        else if (command.equals("model"))
        {
            ts.writeModel(args[1]);
        }
        else if (command.equals("insertTestData"))
        {
            ts.insertTestData(args[1]);
        }
    }
}
